package game;
import java.util.LinkedList;

import exception.IllegalMoveException;

/** This class is used to represent game positions. It uses a 2-dimensional char array for the board and a Boolean
 *  to keep track of which player has the move.
 *
 *  @author Henrik Bj&ouml;rklund
 */


public abstract class AbstractOthelloPosition implements Cloneable{

	/** For a normal Othello game, BOARD_SIZE is 8. */
	public static final int BOARD_SIZE = 8;

	/** True if the first player (white) has the move. */
	protected boolean playerToMove;

	/** The representation of the board. For convenience, the array actually has two columns and two rows more
	that the actual game board. The 'middle' is used for the board. The first index is for rows, and the second
	for columns. This means that for a standard 8x8 game board, <code>board[1][1]</code> represents the
	upper left corner, <code>board[1][8]</code> the upper right corner, <code>board[8][1]</code> the lower
	left corner, and <code>board[8][8]</code> the lower left corner. In the array, the characters 'E', 'W',
	and 'B' are used to represent empty, white, and black board squares, respectively.
	 */
	protected char [][] board;

	public char[][] getBoard(){
		return board;
	}

	/* initialize */

	/** Initializes the position by creating a board, setting all squares to empty and then placing the 
	four middle tiles. */

	public void initialize(){
		board = new char[BOARD_SIZE+2][BOARD_SIZE+2];
		for(int i = 0 ; i < BOARD_SIZE+2 ; i++)
			for(int j = 0 ; j< BOARD_SIZE+2 ; j++)
				board[i][j] = 'E';
		board[BOARD_SIZE/2][BOARD_SIZE/2] = board[BOARD_SIZE/2 +1][BOARD_SIZE/2 +1] = 'W';
		board[BOARD_SIZE/2][BOARD_SIZE/2 +1] = board[BOARD_SIZE/2 +1][BOARD_SIZE/2] = 'B';
		playerToMove = true;
	}

	/* getMoves */

	/** Returns a linked list of <code>OthelloAction</code> representing all possible moves in the position.
	If the list is empty, there are no legal moves for the player who has the move. 
	 */

	public abstract LinkedList<OthelloAction> getMoves();



	/* toMove */

	/** Returns true if the first player (white) has the move, otherwise false. */
	public boolean toMove(){
		return playerToMove;
	}

	/* makeMove  */

	/** Returns the position resulting from making the move <code>action</code> in the current position. 
        Observe that this also changes the player to move next.
	 */
	public abstract AbstractOthelloPosition makeMove(OthelloAction action) throws IllegalMoveException;

	/* illustrate and helper functions */

	/** Draws an ASCII representation of the position. White squares are marked by '0' while 
	black squares are marked by 'X'.
	 */
	public void illustrate(){
		System.out.print("   ");
		for(int i=1 ; i <= BOARD_SIZE ; i++)
			System.out.print("| " + i + " ");
		System.out.println("|");
		printHorizontalBorder();
		for(int i=1 ; i <= BOARD_SIZE ; i++){
			System.out.print(" " + i + " ");
			for(int j=1 ; j <= BOARD_SIZE ; j++){
				if(board[i][j] == 'W'){
					System.out.print("| 0 ");
				}else if(board[i][j] == 'B'){
					System.out.print("| X ");
				}else{
					System.out.print("|   ");
				}
			}
			System.out.println("| " + i + " ");
			printHorizontalBorder();
		}
		System.out.print("   ");
		for(int i=1 ; i <= BOARD_SIZE ; i++)
			System.out.print("| " + i + " ");
		System.out.println("|\n");
	}

	private void printHorizontalBorder(){
		System.out.print("---");
		for(int i = 1 ; i <= BOARD_SIZE ; i++){
			System.out.print("|---");
		}
		System.out.println("|---");
	}
	
	/**
	 * Change the current player
	 */
	public void changePlayer(){
		playerToMove = !playerToMove;
	}

	public Object clone() {
		AbstractOthelloPosition p = null;
		try {
			p = (AbstractOthelloPosition) super.clone();
			p.board = new char[BOARD_SIZE+2][BOARD_SIZE+2];
			for(int i = 0; i <= BOARD_SIZE; i++){
				for(int j = 0; j <= BOARD_SIZE; j++){
					p.board[i][j] = board[i][j];
				}
			}
			p.playerToMove = playerToMove;
		} catch(CloneNotSupportedException cnse) {
			cnse.printStackTrace(System.err);
		}
		return p;
	}
	
	/**
	 * @return a string indicating who is the winner
	 */
	public String getWinner(){
		int nbBlack = 0;
		int nbWhite = 0;
		for(int i = 1; i <= BOARD_SIZE; i++){
			for(int j = 1; j <= BOARD_SIZE; j++){
				if(board[i][j] == 'W'){
					nbWhite++;
				}else if(board[i][j] == 'B'){
					nbBlack++;
				}
			}
		}
		if(nbBlack > nbWhite){
			return "B ("+nbBlack+") vs W ("+nbWhite+")" ;
		}else if(nbWhite > nbBlack){
			return "W ("+nbWhite+") vs B ("+nbBlack+")";
		}else{
			return "Draw : B ("+nbBlack+") vs W ("+nbWhite+")";
		}
	}

	public void setPlayer(boolean white) {
		playerToMove = white;
	}
	
	public boolean isOnlyBlack(){
		return isOnly('B');
	}
	
	public boolean isOnlyWhite(){
		return isOnly('W');
	}
	
	private boolean isOnly(char c){
		for(int i = 1; i <= BOARD_SIZE; i++){
			for(int j = 1; j <= BOARD_SIZE; j++){
				if(board[i][j] != 'E' && board[i][j] != c){
					return false;
				}
			}
		}
		return true;
	}
	
	/**
	 * Count the number of disc for one color
	 * @param c the color
	 * @return
	 */
	public int getCount(char c){
		int sum = 0;
		for(int i = 1; i <= BOARD_SIZE; i++){
			for(int j = 1; j <= BOARD_SIZE; j++){
				if(board[i][j] == c){
					sum++;
				}
			}
		}
		return sum;
	}
	
	/**
	 * Count the number of black disc
	 * @return
	 */
	public int getCountBlack(){
		return getCount('B');
	}
	
	/**
	 * Count the number of white disc
	 * @return
	 */
	public int getCountWhite(){
		return getCount('W');
	}

}
